#include "unixsocket.h"
#ifdef OS_UNIX

int APortableSocket::_total_client_connected = 0;

UNIXSocket::UNIXSocket()
{
   _sock = -1;
   _max_client = 200;
   _client_connected = 0;
   _isServerSock = false;
   _port = 0;
}

UNIXSocket::UNIXSocket(int sock, struct sockaddr_in sin, unsigned short port)
{
    _sock = sock;
    _sin = sin;
    _ip.assign(::inet_ntoa(_sin.sin_addr));
    _port = port;
    _isServerSock = false;
}

bool UNIXSocket::createServerSocket(IPortableSocket::SockType type, unsigned int port)
{
  int         enable;

  _type = type;
  _isServerSock = true;
  enable = 1;
  if (type == IPortableSocket::UDP && ::getprotobyname("udp"))
    _sock = socket(AF_INET, SOCK_DGRAM, ::getprotobyname("udp")->p_proto);
  else
    _sock = socket(AF_INET, SOCK_STREAM, 0);
  setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
  if (_sock == -1)
    {
      std::cout << CREATE_SERVER_ERROR << std::endl;
      return (false);
    }
  _sin.sin_family = AF_INET;
  _sin.sin_port = htons(port);
  _sin.sin_addr.s_addr = INADDR_ANY;
  _ip.assign(::inet_ntoa(_sin.sin_addr));
  if ((bind(_sock, (struct sockaddr*) &_sin, sizeof(struct sockaddr_in)) == -1 || (IPortableSocket::UDP != type && listen(_sock, 42) == -1)))
    {
      std::cout << CREATE_SERVER_ERROR << std::endl;
      return (false);
    }
  return (true);
}

bool UNIXSocket::createClientSock()
{
  _isServerSock = false;
  if (_sock > 0)
    close(_sock);
  _sock = socket(AF_INET, SOCK_STREAM, 0);
  if (_sock < 0)
    return (false);
  std::cout << CONNECTION << std::endl;
  if (::connect(_sock, (sockaddr *) &_sin, sizeof(_sin)) == -1)
    {
      std::cout << CONNECTION_FAILED << std::endl;
      return (false);
    }
  std::cout << CONNECTION_SUCCESS << std::endl;
  _ip.assign(::inet_ntoa(_sin.sin_addr));
  return (true);
}

bool UNIXSocket::connect(std::string const &remote, unsigned int port, IPortableSocket::SockType type)
{
  struct hostent * record = gethostbyname(remote.c_str());
  if (!record)
    return (false);
  in_addr * address = (in_addr * )record->h_addr;
  char * ip_address = inet_ntoa(* address);
  
  std::cout << SERVER_FOUND << ip_address << std::endl;
  
  _sin.sin_addr.s_addr = inet_addr(ip_address);
  _sin.sin_family = AF_INET;
  _sin.sin_port = htons(port);
  _type = type;
  _port = port;
  return (UNIXSocket::createClientSock());
}

UNIXSocket * UNIXSocket::waitForClient()
{
  struct rlimit         l;
  int                   csock;
  int                   client_sin_len;
  struct sockaddr_in	client_sin;

  getrlimit(RLIMIT_NOFILE, &l);
  client_sin_len = sizeof(struct sockaddr_in);
  csock = accept(_sock, (struct sockaddr *) &client_sin, (socklen_t *) &client_sin_len);
  if ((static_cast<unsigned int>(_client_connected) + 10) >= l.rlim_cur ||
      _client_connected == _max_client)
    {
      close(csock);
      return (NULL);
    }
  return (new UNIXSocket(csock, client_sin, _port));
}

unsigned int UNIXSocket::getNumberConnectedClient() const
{
    return (_client_connected);
}

unsigned int UNIXSocket::getTotalNumberConnectedClient() const
{
    return (_total_client_connected);
}

std::string const &UNIXSocket::getIp() const
{
    return (_ip);
}

bool UNIXSocket::isServerSock() const
{
    return (_isServerSock);
}

void UNIXSocket::disconnect()
{
    std::cout << "UNIXSocket::disconnect()" << std::endl;
    if (_sock != -1)
          close(_sock);
}

int UNIXSocket::UNIXGetSocket() const
{
    return (_sock);
}

unsigned short UNIXSocket::getPort() const
{
    return (_port);
}

unsigned int UNIXSocket::read(char *buf, unsigned int size)
{
    int rd;
    if (this->_type == IPortableSocket::UDP)
      {
	struct sockaddr_in from;
	socklen_t from_len = sizeof(struct sockaddr_in);
	rd = ::recvfrom(_sock, static_cast<void *>(buf), size, 0, (struct sockaddr *) &from, &from_len);

	std::cout << from.sin_addr.s_addr << std::endl;

      }
    else
      rd = ::recv(_sock, static_cast<void *>(buf), size, 0);
    if (rd == -1) return (-1);
    return (rd);
}

unsigned int UNIXSocket::read(IPortableSocket *sock, char *buf, unsigned int size)
{
    UNIXSocket *cl = reinterpret_cast<UNIXSocket *> (sock);
    if (!cl)	return (0);
    int rd;
    if (this->_type == IPortableSocket::UDP)
      {
	struct sockaddr_in from;
	socklen_t from_len = sizeof(struct sockaddr_in);
	rd = ::recvfrom(cl->_sock, static_cast<void *>(buf), size, 0, (struct sockaddr *) &from, &from_len);
      }
    else
      rd = ::recv(cl->_sock, static_cast<void *>(buf), size, 0);
    if (rd == -1) return (-1);
    return (rd);
}

void UNIXSocket::send(IPortableSocket *sock, std::string const & str)
{
    UNIXSocket::send(sock, const_cast<char *>(str.c_str()), str.length());
}

void UNIXSocket::send(IPortableSocket *sock, const char *str, unsigned int len)
{
    UNIXSocket *convert = reinterpret_cast<UNIXSocket *> (sock);
    if (convert->_type == IPortableSocket::UDP)
      {
	struct sockaddr from;
	socklen_t	from_len;
	::sendto(convert->_sock, (str), len, MSG_DONTWAIT, &from, from_len);
      }
    else
      ::send(convert->_sock, str, len, 0);
}

void UNIXSocket::send(std::string const &str)
{
    UNIXSocket::send(str.c_str(), str.length());
}

void UNIXSocket::send(const char *str, unsigned int len)
{
    if (_sock != -1)
      {
	if (_sock == IPortableSocket::UDP)
	  {
	    struct sockaddr from;
	    socklen_t	from_len;
	    ::sendto(_sock, (str), len, MSG_DONTWAIT, &from, from_len);
	  }
	else
	  ::send(_sock, str, len, 0);
      }
}

void UNIXSocket::setMaxClient(unsigned int nb)
{
    _max_client = nb;
}

#endif
